﻿//AcademicPlanner - course registration planning web app.
//Copyright (C) 2009  Boubacar Diallo

//This program is free software; you can redistribute it and/or
//modify it under the terms of the GNU General Public License
//as published by the Free Software Foundation; either version 2
//of the License, or (at your option) any later version.

//This program is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU General Public License for more details.

//You should have received a copy of the GNU General Public License
//along with this program; if not, write to the Free Software
//Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.


using System;
using System.Collections.Generic;
using AcademicPlanner.Domain.Models;
using AcademicPlanner.Data;
using AcademicPlanner.Data.Repositories;
using AcademicPlanner.Data.Entities;

namespace AcademicPlanner.Domain
{
	/// <summary>
	/// ModelsFountain is the singleton class that serves as a factory of
	/// <c>Model</c>s. It also internally caches the <c>Model</c>s it fabricates.
	/// </summary>
	/// <version>1.0</version>
	/// <since>1.0</since>
	class ModelsFountain
	{
		private Dictionary<string, Model> models = new Dictionary<string, Model>();


		/// <summary>
		/// Gets the <c>DomainMaster</c> of the AcademicPlanner application.
		/// </summary>
		/// <version>1.0</version>
		/// <since>1.0</since>
		internal DomainMaster DomainMaster { get { return DomainMaster.GetInstance(); } }


		private ModelsFountain()
		{
		}

		private class InstanceHolder
		{
			internal static ModelsFountain INSTANCE = new ModelsFountain();
		}


		/// <summary>
		/// Gets the single ModelsFountain instance. 
		/// </summary>
		/// <returns>The single ModelsFountain instance.</returns>
		/// <version>1.0</version>
		/// <since>1.0</since>
		internal static ModelsFountain GetInstance()
		{
			return InstanceHolder.INSTANCE;
		}


		/// <summary>
		/// Gets or creates the <c>Model</c> represented by the given <c>Entity</c>.
		/// This method tries to read from its cache first before creating the <c>Model</c>
		/// by reflection.
		/// </summary>
		/// <typeparam name="TModel">The type of the <c>Model</c> to get/create.</typeparam>
		/// <param name="pEntity">The <c>Entity</c> representing the <c>Model</c> to get/create.</param>
		/// <returns>The <c>Model</c> represented by the given <c>Entity</c>.</returns>
		/// <version>1.0</version>
		/// <since>1.0</since>
		internal TModel GetModel<TModel>(Entity pEntity) where TModel : Model
		{
			TModel rMod = null;
			Model vWaste;
			string vKey = string.Format("{0}-{1}", typeof(TModel).Name, pEntity.Id);

			models.TryGetValue(vKey, out vWaste);
			if (vWaste != null) rMod = (TModel)vWaste;
			else
			{
				rMod = (TModel)Activator.CreateInstance(typeof(TModel), pEntity);
				models.Add(vKey, rMod);
			}

			return rMod;
		}

		internal void LoseModel(Model pModel) 
		{
			string vKey = string.Format("{0}-{1}", pModel.GetType().Name, pModel.EntityId);
			if (models.ContainsKey(vKey))
				models.Remove(vKey);
		}

		/// <summary>
		/// Gets or creates a <c>Person</c>. This method is a convenience call
		/// to <see cref="M:GetModel(TModel)"/>.
		/// </summary>
		/// <param name="pEntity">The <c>PersonEntity</c> representing the <c>Person</c> to get/create.</param>
		/// <returns>The <c>Person</c> represented by the given <c>PersonEntity</c>.</returns>
		/// <seealso cref="M:GetModel(TModel)"/>
		/// <version>1.0</version>
		/// <since>1.0</since>
		internal Person GetPerson(PersonEntity pEntity)
		{
			return GetModel<Person>(pEntity);
		}

		/// <summary>
		/// Gets or creates a <c>User</c>. This method is a convenience call
		/// to <see cref="M:GetModel(TModel)"/>.
		/// </summary>
		/// <param name="pEntity">The <c>UserEntity</c> representing the <c>User</c> to get/create.</param>
		/// <returns>The <c>User</c> represented by the given <c>UserEntity</c>.</returns>
		/// <seealso cref="M:GetModel(TModel)"/>
		/// <version>1.0</version>
		/// <since>1.0</since>
		internal User GetUser(UserEntity pEntity)
		{
			return GetModel<User>(pEntity);
		}

		/// <summary>
		/// Gets or creates a <c>Account</c>. This method is a convenience call
		/// to <see cref="M:GetModel(TModel)"/>.
		/// </summary>
		/// <param name="pEntity">The <c>AccountEntity</c> representing the <c>Account</c> to get/create.</param>
		/// <returns>The <c>Account</c> represented by the given <c>AccountEntity</c>.</returns>
		/// <seealso cref="M:GetModel(TModel)"/>
		/// <version>1.0</version>
		/// <since>1.0</since>
		internal Account GetAccount(AccountEntity pEntity)
		{
			return GetModel<Account>(pEntity);
		}

		internal AcademicPlan GetAcademicPlan(AcademicPlanEntity pEntity)
		{
			return GetModel<AcademicPlan>(pEntity);
		}

		internal Planner GetPlanner(PlannerEntity pEntity)
		{
			return GetModel<Planner>(pEntity);
		}

		/// <summary>
		/// Creates a new <c>Person</c>. This method requests a new <c>PersonEntity</c>
		/// from the data layer and creates a <c>Person</c> based on it.
		/// </summary>
		/// <returns>A new <c>Person</c>.</returns>
		/// <seealso cref="P:DomainMaster.DSAS"/>
		/// <seealso cref="P:AcademicPlanner.Data.DataSourceAccessSupervisor.PersonEntityRepo"/>
		/// <seealso cref="M:AcademicPlanner.Data.Repositories.PersonEntityRepository.GetNew"/>
		/// <version>1.0</version>
		/// <since>1.0</since>
		internal Person GetNewPerson()
		{
			Person rPers = null;
			var vEnt = DomainMaster.DSAS.PersonEntityRepo.GetNew();

			rPers = new Person(vEnt);

			return rPers;
		}

		/// <summary>
		/// Creates a new <c>Account</c>. This method requests a new <c>AccountEntity</c>
		/// from the data layer and creates a <c>Account</c> based on it.
		/// </summary>
		/// <returns>A new <c>Account</c>.</returns>
		/// <seealso cref="P:DomainMaster.DSAS"/>
		/// <seealso cref="P:AcademicPlanner.Data.DataSourceAccessSupervisor.AccountEntityRepo"/>
		/// <seealso cref="M:AcademicPlanner.Data.Repositories.AccountEntityRepository.GetNew"/>
		/// <version>1.0</version>
		/// <since>1.0</since>
		internal Account GetNewAccount()
		{
			Account rMod = null;
			var vEnt = DomainMaster.DSAS.AccountEntityRepo.GetNew();

			rMod = new Account(vEnt);

			return rMod;
		}

		internal Planner GetNewPlanner()
		{
			var vEnt = DomainMaster.DSAS.PlannerEntityRepo.GetNew();
			var rMod = new Planner(vEnt);

			return rMod;
		}

		internal CourseDivision GetNewCourseDivision()
		{
			var vEnt = DomainMaster.DSAS.CourseDivisionEntityRepo.GetNew();
			var rMod = new CourseDivision(vEnt);

			return rMod;
		}

		internal CourseCategory GetNewCourseCategory()
		{
			var vEnt = DomainMaster.DSAS.CourseCategoryEntityRepo.GetNew();
			var rMod = new CourseCategory(vEnt);

			return rMod;
		}

		internal AcademicTerm GetNewAcademicTerm()
		{
			var vEnt = DomainMaster.DSAS.AcademicTermEntityRepo.GetNew();
			var rMod = new AcademicTerm(vEnt);

			return rMod;
		}

		internal Program GetNewProgram()
		{
			var vEnt = DomainMaster.DSAS.ProgramEntityRepo.GetNew();
			var rMod = new Program(vEnt);

			return rMod;
		}

		internal Course GetNewCourse()
		{
			var vEnt = DomainMaster.DSAS.CourseEntityRepo.GetNew();
			var rMod = new Course(vEnt);

			return rMod;
		}

		internal CourseCategory GetCourseCategory(CourseCategoryEntity pEntity)
		{
			return GetModel<CourseCategory>(pEntity);
		}

		internal CourseDivision GetCourseDivision(CourseDivisionEntity pEntity)
		{
			return GetModel<CourseDivision>(pEntity);
		}

		internal Program GetProgram(ProgramEntity pEntity)
		{
			return GetModel<Program>(pEntity);
		}

		internal AcademicSchedule GetNewAcademicSchedule()
		{
			var vEnt = DomainMaster.DSAS.AcademicScheduleEntityRepo.GetNew();
			var rMod = new AcademicSchedule(vEnt);

			return rMod;
		}

		internal AcademicTerm GetAcademicTerm(AcademicTermEntity pEntity)
		{
			return GetModel<AcademicTerm>(pEntity);
		}

		internal AcademicSchedule GetAcademicSchedule(AcademicScheduleEntity pEntity)
		{
			return GetModel<AcademicSchedule>(pEntity);
		}

		internal Course GetCourse(CourseEntity pEntity)
		{
			return GetModel<Course>(pEntity);
		}

		internal Registration GetNewRegistration()
		{
			var vEnt = DomainMaster.DSAS.RegistrationEntityRepo.GetNew();
			var rMod = new Registration(vEnt);

			return rMod;
		}
	}
}
